/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hxg.portmgr.core.dao;

import hxg.portmgr.core.device.Device;
import hxg.portmgr.core.device.DeviceModel;
import hxg.portmgr.core.device.DeviceState;
import hxg.portmgr.core.device.DeviceType;
import hxg.portmgr.core.ip.IpAddressEntry;
import hxg.portmgr.core.oper.snmp.SnmpOper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import util.Sequence;

/**
 *
 * @author hxg
 */
public abstract class AbstractSnmpDeviceDao implements DeviceDAO {

    protected Map<Long, Device> deviceMap = new ConcurrentHashMap<>();

    /**
     *
     * @param devs
     * @param threadCount 并发线程数，取值范围为1-128，小等于0的话设为8，大于128的话设为128
     * @param isSyn 是否同步模式，在同步模式下，必须全部设备扫描完才返回
     * @return 一个AtomicInteger，其表示已完成的线程数
     */
    public AtomicInteger addDeviceAndDetectModel(final List<Device> devs, int threadCount, boolean isSyn) {
        final AtomicInteger atomCount = new AtomicInteger(0);
        if (threadCount <= 0) {
            threadCount = 8;
        }
        if (threadCount > 128) {
            threadCount = 128;
        }
        ExecutorService service = Executors.newFixedThreadPool(threadCount);
        List<Future<Integer>> futures = new ArrayList<>();
        for (final Device dev : devs) {
            Future<Integer> f = service.submit(new Callable<Integer>() {
                @Override
                public Integer call() {
                    try {
                        System.out.println("Device=:" + dev.getIp() + "|" + dev.getChsName());
                        SnmpOper.scanDeviceModel(dev);
                        //dev.setState(DeviceState.Online);
                    } catch (Exception ex) {
                        //dev.setState(DeviceState.OffLine);
                        Logger.getLogger(AbstractSnmpDeviceDao.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    System.out.println("Device=:" + dev.getIp() + "|" + dev.getChsName() + "|" + dev.getModel().toString());
                    System.out.println("Count=:" + atomCount.get());
                    addDevice(dev);
                    int cr = atomCount.incrementAndGet();
                    return cr;
                }
            });
            /**
             * 这里在前面有一个判断，仅当要求了同步的方式时，才向futures:List<Future>添加Future
             */
            if (isSyn) {
                futures.add(f);
            }
        }
        service.shutdown();
        /**
         * 这里在前面有一个判断，仅当要求了同步的方式时，才向futures:List<Future>添加Future,所以在异步模式下，List是空的
         */
        for (Future f : futures) {
            try {
                f.get();
            } catch (InterruptedException | ExecutionException ex) {
                Logger.getLogger(AbstractSnmpDeviceDao.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return atomCount;
    }

    public void addDeviceAndDetectModel(final Device dev, ExecutorService service) {
        if (service == null || service.isTerminated() || service.isShutdown()) {
            //dev.setState(DeviceState.OffLine);
        } else {
            service.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        SnmpOper.scanDeviceModel(dev);
                        //dev.setState(DeviceState.Online);
                    } catch (IOException ex) {
                        //dev.setState(DeviceState.OffLine);
                        Logger.getLogger(AbstractSnmpDeviceDao.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    addDevice(dev);
                }
            });
        }

    }

    public void addDeviceAndDetectModel(final Device dev) {
        try {
            SnmpOper.scanDeviceModel(dev);
            //dev.setState(DeviceState.Online);

        } catch (IOException ex) {
            //dev.setState(DeviceState.OffLine);
            Logger.getLogger(AbstractSnmpDeviceDao.class.getName()).log(Level.SEVERE, null, ex);
        }
        addDevice(dev);
    }

    @Override
    public void addDevice(Device dev) {
        deviceMap.put(dev.getId(), dev);
    }

    @Override
    public void removeDevice(Device dev) {
        deviceMap.remove(dev.getId());
    }

    @Override
    public Device getDeviceByIp(String ip) {
        //List<Device> list = new ArrayList<>();
        //首先直接返回有的
        for (Device dev : deviceMap.values()) {
            if (ip.trim().equals(dev.getIp())) {
                //做一次可达检查
                if (dev.getModel() == DeviceModel.getUnReachedAbleModel()) {
                    try {
                        SnmpOper.scanDeviceModel(dev);
                    } catch (IOException ex) {
                        Logger.getLogger(AbstractSnmpDeviceDao.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                return dev;
            }
        }
        //如果没有直接有，再去搜索
        for (Device dev : deviceMap.values()) {
            for (IpAddressEntry ipe : dev.getIps()) {
                if (ip.equals(ipe.getIpAddr().getIpAddr())) {
                    //做一次可达检查
                    if (dev.getModel() == DeviceModel.getUnReachedAbleModel()) {
                        try {
                            SnmpOper.scanDeviceModel(dev);
                        } catch (IOException ex) {
                            Logger.getLogger(AbstractSnmpDeviceDao.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                    return dev;
                }
            }
        }
        Device dev = new Device();
        dev.setIp(ip);
        Sequence seq = new Sequence();
        dev.setId(seq.getSeq());
        try {
            SnmpOper.scanDeviceModel(dev);
        } catch (IOException ex) {
            Logger.getLogger(AbstractSnmpDeviceDao.class.getName()).log(Level.SEVERE, null, ex);
        }
        return dev;
    }

    @Override
    public Device getDeviceById(long devId) {
        return deviceMap.get(devId);
    }

    @Override
    public void addDevices(Collection<Device> devs) {
        for (Device dev : devs) {
            addDevice(dev);
        }
    }

    @Override
    public List<Device> getDevices() {
        List<Device> list = new ArrayList<>();
        list.addAll(deviceMap.values());
        return list;
    }

    @Override
    public List<Device> getDevices(DeviceType type) {
        List<Device> list = new ArrayList<>();
        for (Device dev : deviceMap.values()) {
            if (type == dev.getType()) {
                list.add(dev);
            }
        }
        return list;
    }

    @Override
    public List<Device> getDevices(DeviceModel model) {
        List<Device> list = new ArrayList<>();
        for (Device dev : deviceMap.values()) {
            if (model == dev.getModel()) {
                list.add(dev);
            }
        }
        return list;
    }
}
